import crypt from "./api_crypt"
import abyte from "./api_byte"
import etype from "./lua_binarytype"



export default class O extends etype{
    private static blob:ArrayBuffer = new ArrayBuffer(2^16); 
    private static view:DataView = new DataView(O.blob);
    private static byte:abyte = new abyte(O.blob)
    //编码
    public static encode(name:string|number|null,table:object|any):ArrayBuffer{
        //添加键值
        O.setValueOf(name)
        //表的开头
        O.setTag(O.luaLeft)
        //遍历编码
        for (const key in table) {
            if (Object.prototype.hasOwnProperty.call(table, key)) {
                const val = table[key];
                //填充key
                if(false == O.IfObject(key)){
                    O.setValueOf(key)
                }
                else{
                    O.encode(null,key)
                }
                //填充vaule
                if(false == O.IfObject(val)){
                    O.setValueOf(val)
                }
                else{
                    O.encode(null,val)
                }
            }
        }

        //表的结尾
        O.setTag(O.luaRigh)
        return O.byte.buffer()
    }

    //解码
    public static decode(blob:ArrayBuffer):object{
        let table:object = {}
        let byte:abyte = new abyte(blob)
        let bValue:boolean = false;
        let kValue = null
        let vValue = null
        while(byte.lenght() < blob.byteLength){
            let tag = byte.getInt8(byte.lenght())
            switch(tag){
                case O.luaFalse:vValue=false;break;
                case O.luaTrue:vValue=true;break;
                case O.luaFloat:vValue=byte.getNumber(byte.lenght());break;
                case O.luaString:vValue=byte.getString(byte.lenght());break;
                case O.luaLeft:vValue=O.decode(blob);bValue = false;break;
                case O.luaRigh:bValue = true;break;
                case O.luaInt08:vValue=byte.getInt8(byte.lenght());break;
                case O.luaInt16:vValue=byte.getInt16(byte.lenght());break;
                case O.luaInt24:vValue=byte.getInt24(byte.lenght());break;
                case O.luaInt32:vValue=byte.getInt32(byte.lenght());break;
                case O.luaInt40:vValue=byte.getInt40(byte.lenght());break;
                case O.luaInt48:vValue=byte.getInt48(byte.lenght());break;
                case O.luaInt56:vValue=byte.getInt56(byte.lenght());break;
                case O.luaInt64:vValue=byte.getInt64(byte.lenght());break;
            }

            if(false == bValue){
                kValue = vValue
            }else{
                table[kValue] = vValue
            }

            bValue = !bValue;

        }
        return table
    }

    //编码
    public static safeEncode(name:string,data:any){
        let buffer = O.encode(name,data)
        return crypt.encrypt(buffer)
    }

    //解码
    public static safeDecode(blob:ArrayBuffer){
        let buffer = crypt.decrypt(blob)
        return O.decode(blob)
    }

    public static setTag(tag:number){
        O.byte.setInt8(tag);
    }

    //布尔字段
    public static setBoolean(value:boolean){
        if(true == value){
            O.byte.setInt8(O.luaTrue)
        }else{
            O.byte.setInt8(O.luaFalse)
        }
    }

    //数字字段
    public static setNumber(value:number){
        let bit = this.getNumberSize(value);
        let tag = this.getNumberType(value,bit);

        //设置标志
        if(false == O.IS_NV(value)){
            O.byte.setInt8(tag);
        }

        //浮点数
        if(tag == O.luaFloat){
            O.byte.setFloat64(value);
        }
        //特殊值
        else if(O.IS_NV(value)){
            O.byte.setInt8(O.AS_NT(value)); 
        }
        //整数
        else{
            switch(tag){
                case O.luaInt08:O.byte.setInt8(value);break;
                case O.luaInt16:O.byte.setInt16(value);break;
                case O.luaInt24:O.byte.setInt24(value);break;
                case O.luaInt32:O.byte.setInt32(value);break;
                case O.luaInt40:O.byte.setInt40(value);break;
                case O.luaInt48:O.byte.setInt48(value);break;
                case O.luaInt56:O.byte.setInt56(value);break;
                case O.luaInt64:O.byte.setInt64(value);break;
            }
        }
    }

    public static getNumberSize(value:number):number{
        if(0 == value%1 && value >= 0){
            let uint = Math.abs(value);
            for(let i = 0;i < 64;i++){
                if(Math.pow(2,i) > (uint+1)){
                    return Math.floor(i/8)+1;
                }
            }
        }
        return 8;
    }

    public static getNumberType(v:number,tag:number):number{
        if(O.IfFloat(v)){
            return O.luaFloat;
        }
        return  O.luaInt08 + tag - 1;
    }
    
    public static setString(value:string){
        this.byte.setInt8(O.luaString)
        let len = value.length;
        for(let i=0; i<len; i++) {
            let c = value.charCodeAt(i);
            if (c < (1<<7)) {
                this.byte.setInt8(c);
                continue;
            }
            if (c < (1<<11)) {
                
                this.byte.setUint8((c >> 6) | 0xC0);
                this.byte.setUint8(c & 0x3F | 0x80);
                continue;
            }
            if (c < (1<<17)) {
                this.byte.setUint8((c >> 12) | 0xE0);
                this.byte.setUint8((c >> 6) & 0x3F | 0x80);
                this.byte.setUint8((c & 0x3F | 0x80));
                continue;
            }
            if (c < (1<<23)) {
                this.byte.setUint8((c >> 18) | 0xF0);
                this.byte.setUint8((c >> 12) & 0x3F | 0x80);
                this.byte.setUint8((c >> 6) & 0x3F | 0x80);
                this.byte.setUint8((c & 0x3F) | 0x80);
                continue;
            }
            if (c < (1<<29)) {
                this.byte.setUint8((c >> 24) | 0xF8);
                this.byte.setUint8((c >> 18) & 0x3F | 0x80);
                this.byte.setUint8((c >> 12) & 0x3F | 0x80);
                this.byte.setUint8((c >> 6) & 0x3F | 0x80);
                this.byte.setUint8(c & 0x3F | 0x80);
                continue;
            }
        }

        this.byte.setUint8(0);
    }

    //写入数据
    public static setValueOf(value:any){
        if(null != value){
            if(O.IfBoolean(value)){
                O.setBoolean(value);
            }else if(O.IfNumber(value)){
                O.setNumber(value);
            }else if(O.IfString(value)){
                O.setString(value);
            }
        }
    }
}